package znet

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/jiangjiali/z-tcp/helper/gmsm/sm2"
	"gitee.com/jiangjiali/z-tcp/utils"
	"gitee.com/jiangjiali/z-tcp/ziface"
	"io"
	"net"
	"sync"
	"time"
)

type Connection struct {
	//当前Conn属于哪个Server
	TcpServer ziface.IServer
	//当前连接的socket TCP套接字
	Conn *net.TCPConn
	//当前连接的ID 也可以称作为SessionID，ID全局唯一
	ConnID int64
	//消息管理MsgId和对应处理方法的消息管理模块
	MsgHandler ziface.IMsgHandle
	//告知该链接已经退出/停止的channel
	ctx    context.Context
	cancel context.CancelFunc
	//无缓冲管道，用于读、写两个goroutine之间的消息通信
	msgChan chan []byte
	//有缓冲管道，用于读、写两个goroutine之间的消息通信
	msgBuffChan chan []byte

	sync.RWMutex
	//链接属性
	property map[string]interface{}
	//保护当前property的锁
	propertyLock sync.Mutex
	//当前连接的关闭状态
	isClosed bool
	// 最近一次心跳时间
	lastHeartbeatTime time.Time
	//服务端的密钥信息
	ServerPem utils.ServerPem
	//服务端的私钥-用于解密数据
	SPrivateKey sm2.PrivateKey
	//客户端的公钥-用于加密数据
	CPublicKey sm2.PublicKey
	// 是否第一次读取数据
	isFirstReader chan bool
}

// NewConntion 创建连接的方法
func NewConntion(server ziface.IServer, conn *net.TCPConn, connID int64, msgHandler ziface.IMsgHandle) *Connection {
	// 生成密钥对信息
	serverPem, sPrivateKey := utils.GeneratePem(connID)
	//初始化Conn属性
	c := &Connection{
		TcpServer:     server,
		Conn:          conn,
		ConnID:        connID,
		isClosed:      false,
		MsgHandler:    msgHandler,
		msgChan:       make(chan []byte),
		msgBuffChan:   make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
		isFirstReader: make(chan bool),
		property:      make(map[string]interface{}),
		SPrivateKey:   *sPrivateKey,
		ServerPem:     serverPem,
	}

	//将新创建的Conn添加到链接管理中
	c.TcpServer.GetConnMgr().Add(c)
	return c
}

/*
	StartWriter 写消息Goroutine， 用户将数据发送给客户端
*/
func (c *Connection) StartWriter() {
	fmt.Println("[写消息] 启动写消息线程")
	defer fmt.Println("[写消息] 写消息的连接停止 地址:", c.RemoteAddr().String())

	for {
		select {
		case data := <-c.msgChan:
			//有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("[写消息] 写消息的连接错误:", err)
				return
			}
		case data, ok := <-c.msgBuffChan:
			if ok {
				//有数据要写给客户端
				if _, err := c.Conn.Write(data); err != nil {
					fmt.Println("[写消息] 写带缓存数据的连接错误:", err)
					return
				}
			} else {
				fmt.Println("[写消息] msgBuffChan 通道关闭")
				break
			}
		case <-c.ctx.Done():
			return
		}
	}
}

/*
	StartReader 读消息Goroutine，用于从客户端中读取数据
*/
func (c *Connection) StartReader() {
	fmt.Println("[读信息] 启动读消息线程")
	defer fmt.Println("[读信息] 读消息的连接停止 地址:", c.RemoteAddr().String())
	defer c.Stop()

	for {
		select {
		case <-c.ctx.Done():
			return
		default:
			//读取客户端的Msg head
			headData := make([]byte, c.TcpServer.Packet().GetHeadLen())
			if _, err := io.ReadFull(c.Conn, headData); err != nil {
				fmt.Println("[读信息] 读取信息错误:", err)
				return
			}

			//拆包，得到msgid 和 datalen 放在msg中
			msg, err := c.TcpServer.Packet().Unpack(headData)
			if err != nil {
				fmt.Println("[读信息] 拆包错误:", err)
				return
			}

			//网关通信ID 约定为 101
			if msg.GetMsgId() == 101 {
				c.isFirstReader <- true
			}

			//根据 dataLen 读取 data，放在msg.Data中
			var data []byte
			if msg.GetDataLen() > 0 {
				data = make([]byte, msg.GetDataLen())
				if _, err := io.ReadFull(c.Conn, data); err != nil {
					fmt.Println("[读信息] 读消息的连接错误:", err)
					return
				}
			}

			//服务端私钥-解密数据
			plaintxt, err := c.SPrivateKey.DecryptAsn1(data)
			if err != nil {
				fmt.Println("[读信息] 服务端私钥解密数据错误:", err)
				return
			}

			//设置数据
			msg.SetData(plaintxt)

			//得到当前客户端请求的Request数据
			req := Request{
				conn: c,
				msg:  msg,
			}

			if utils.GlobalObject.WorkerPoolSize > 0 {
				//已经启动工作池机制，将消息交给Worker处理
				c.MsgHandler.SendMsgToTaskQueue(&req)
			} else {
				//从绑定好的消息和对应的处理方法中执行对应的Handle方法
				go c.MsgHandler.DoMsgHandler(&req)
			}
		}
	}
}

// Start 启动连接，让当前连接开始工作
func (c *Connection) Start() {
	c.ctx, c.cancel = context.WithCancel(context.Background())
	// 心跳检测线程
	go c.heartbeatChecker()
	//1 开启用户从客户端读取数据流程的Goroutine
	go c.StartReader()
	//2 开启用于写回客户端数据流程的Goroutine
	go c.StartWriter()
	// 3 发送服务端公钥给客户端
	if serverPem, err := json.Marshal(c.ServerPem); err != nil {
		return
	} else {
		if err = c.SendServerPem(101, serverPem); err != nil {
			return
		}
	}
	// 4 在规定时间内，检测客户端初次加密通信
	for {
		select {
		case <-c.ctx.Done():
			return
		case <-c.isFirstReader:
			//按照用户传递进来的创建连接时需要处理的业务，执行钩子方法
			c.TcpServer.CallOnConnStart(c)
			fmt.Println("[启动连接] 加密通信配对成功! 连接ID:", c.ConnID)
			close(c.isFirstReader) // 关闭通道
			return
		case <-time.After(time.Second * utils.GlobalObject.PairTime):
			fmt.Println("[启动连接] 加密通信配对失败! 连接ID:", c.ConnID)
			c.Stop()
			return
		}
	}
}

// Stop 停止连接，结束当前连接状态M
func (c *Connection) Stop() {
	//如果用户注册了该链接的关闭回调业务，那么在此刻应该显示调用
	c.TcpServer.CallOnConnStop(c)

	c.Lock()
	defer c.Unlock()

	//如果当前链接已经关闭
	if c.isClosed == true {
		return
	}

	fmt.Println("[停止] 停止连接! 连接ID:", c.ConnID)

	// 关闭socket链接
	err := c.Conn.Close()
	if err != nil {
		fmt.Println("[停止] 关闭连接失败! 连接ID:", c.ConnID)
		return
	}
	//关闭Writer
	c.cancel()

	//将链接从连接管理器中删除
	c.TcpServer.GetConnMgr().Remove(c)

	//关闭该链接全部管道
	close(c.msgChan)
	close(c.msgBuffChan)
	//设置标志位
	c.isClosed = true
}

// GetTCPConnection 从当前连接获取原始的socket TCPConn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

func (c *Connection) SetSPublicKey(key sm2.PublicKey) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	c.CPublicKey = key
}

// GetTcpServer 获取服务端句柄
func (c *Connection) GetTcpServer() ziface.IServer {
	return c.TcpServer
}

// GetConnID 获取当前连接ID
func (c *Connection) GetConnID() int64 {
	return c.ConnID
}

// RemoteAddr 获取远程客户端地址信息
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// SendServerPem 直接将服务端公钥发送给远程的TCP客户端
func (c *Connection) SendServerPem(msgId uint32, data []byte) error {
	c.RLock()
	if c.isClosed == true {
		c.RUnlock()
		return errors.New("[服务端公钥发送] 发送信息时，连接关闭了")
	}
	c.RUnlock()

	//将加密data封包，并且发送
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("[服务端公钥发送] 封包错误! 信息Id:", msgId)
		return errors.New("[服务端公钥发送] 封包错误")
	}

	//写回客户端
	c.msgChan <- msg

	return nil
}

// SendMsg 直接将Message数据发送数据给远程的TCP客户端
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	c.RLock()
	if c.isClosed == true {
		c.RUnlock()
		return errors.New("[发送信息] 发送信息时，连接关闭了")
	}
	c.RUnlock()

	if utils.IsNil(c.CPublicKey) {
		c.Stop()
		return errors.New("[发送信息] 没有拿到客服端公钥")
	}

	//客户端公钥去加密数据
	ciphertxt, err := c.CPublicKey.EncryptAsn1(data, rand.Reader) //加密数据
	if err != nil {
		return errors.New("[发送信息] 客服端公钥加密数据失败")
	}

	//将加密data封包，并且发送
	dp := c.TcpServer.Packet()
	msg, err := dp.Pack(NewMsgPackage(msgId, ciphertxt))
	if err != nil {
		fmt.Println("[发送信息] 封包错误! 信息Id:", msgId)
		return errors.New("[发送信息] 封包错误")
	}

	//写回客户端
	c.msgChan <- msg

	return nil
}

func (c *Connection) SendBuffMsg(msgId uint32, data []byte) error {
	c.RLock()
	if c.isClosed == true {
		c.RUnlock()
		return errors.New("[发送带缓存信息] 发送信息时，连接关闭了")
	}
	c.RUnlock()

	if utils.IsNil(c.CPublicKey) {
		c.Stop()
		return errors.New("[发送信息] 没有拿到客服端公钥")
	}

	//客户端公钥去加密数据
	ciphertxt, err := c.CPublicKey.EncryptAsn1(data, rand.Reader) //加密数据
	if err != nil {
		return errors.New("[发送带缓存信息] 客服端公钥加密数据失败")
	}

	//将加密data封包，并且发送
	dp := c.TcpServer.Packet()
	msg, err := dp.Pack(NewMsgPackage(msgId, ciphertxt))
	if err != nil {
		fmt.Println("[发送带缓存信息] 封包错误! 信息Id:", msgId)
		return errors.New("[发送带缓存信息] 封包错误")
	}

	//写回客户端
	c.msgBuffChan <- msg

	return nil
}

// SetProperty 设置链接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	c.property[key] = value
}

// GetProperty 获取链接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	if value, ok := c.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("[获取连接属性] 没找到属性")
	}
}

// RemoveProperty 移除链接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	delete(c.property, key)
}

// 心跳检车模块---------------------------------------------------------
// 每隔60秒, 检查一次连接是否健康
func (c *Connection) heartbeatChecker() {
	var (
		timer *time.Timer
	)
	timer = time.NewTimer(time.Duration(utils.GlobalObject.HeartbeatInterval) * time.Second)
	for {
		select {
		case <-timer.C:
			if !c.IsAlive() {
				err := c.Conn.Close()//断开连接
				if err == nil {
					goto EXIT
				}
			}
			timer.Reset(time.Duration(utils.GlobalObject.HeartbeatInterval) * time.Second)
		}
	}
EXIT:
	timer.Stop()
}

// IsAlive 心跳激活判断
func (c *Connection) IsAlive() bool {
	var	now = time.Now()

	c.Lock()
	defer c.Unlock()

	// 连接已关闭 或者 太久没有心跳
	if c.isClosed || now.Sub(c.lastHeartbeatTime) > time.Duration(utils.GlobalObject.HeartbeatInterval)*time.Second {
		return false
	}
	return true
}

// KeepAlive 更新心跳
func (c *Connection) KeepAlive() {
	var	now = time.Now()

	c.Lock()
	defer c.Unlock()

	c.lastHeartbeatTime = now
}

// Gate 网关模块 客户端请求格式------------------
type Gate struct {
	Name      string `json:"name"`       //客户端名字
	UA        []byte `json:"ua"`         // 连接统一认证码
	Sign      []byte `json:"sign"`       // 认证码的签名
	PublicKey []byte `json:"public-key"` // 客户端的公钥
}
